package com.hk.commons.redis;

import com.fasterxml.jackson.core.type.TypeReference;
import com.hk.commons.utils.BeanConvertUtil;
import com.hk.commons.utils.JacksonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Component
public class RedisUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(RedisUtil.class);

    @Resource(name = "redisTemplate")
    private RedisTemplate<String,Object> redisTemplate;

    /**
     * 删除key-v
     */
    public void delKey(String key){
        redisTemplate.delete(key);
    }

    /**
     * 设置key-string
     */
    public void setString(String key, String value) {
        redisTemplate.opsForValue().set(key,value);

    }

    /**
     * 设置key-obj
     */
    public void setObj(String key, Object value) {
        redisTemplate.opsForValue().set(key,value);

    }

    /**
     * 设置key-obj
     */
    public void setObj(String key, Object value,Long timeOutSeconds) {
        redisTemplate.opsForValue().set(key,value,timeOutSeconds, TimeUnit.SECONDS);

    }


    /**
     * 设置key-string 过期时间
     */
    public void setString(String key, String value, Long timeOutSeconds) {
        redisTemplate.opsForValue().set(key,value,timeOutSeconds, TimeUnit.SECONDS);

    }

    /**
     * 设置redis 自增
     */
    public Long increment(String key) {
        return redisTemplate.opsForValue().increment(key);
    }

    /**
     * 设置redis 自增 以及自增初始大小
     */
    public Long increment(String key,Long delat) {
        return redisTemplate.opsForValue().increment(key, delat);
    }

    /**
     * 判断redis中是否存在此key
     */
    public boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 设置key 过期时间（单位：秒）
     */
    public void setExpire(String key,Long timeoutSecond){
        redisTemplate.expire(key,timeoutSecond, TimeUnit.SECONDS);
    }


    /**
     * 根据key获取value
     */
    public Object getValue(String key){
        return redisTemplate.opsForValue().get(key);
    }

    public <T> T getObj(final String key, final Class<T> clazz) {
        return redisTemplate.execute((RedisCallback<T>) connection -> {
            byte[] keybyte = serializeKey(key);
            if (connection.exists(keybyte)) {
                byte[] bs = connection.get(keybyte);
                return deserializeValue(bs, clazz);
            } else {
                return null;
            }
        });
    }

    public <T> List<T> mget(Set<String> keys,Class<T> clazz){
        List<Object> list = redisTemplate.opsForValue().multiGet(keys);
        return BeanConvertUtil.copyList(list,clazz);
    }

    /**
     * put hash数据
     * @param key redis key
     * @param hashKey hash key
     * @param value hash value
     */
    public <K,V> void putHash(String key,K hashKey,V value){
        redisTemplate.opsForHash().put(key,hashKey,value);

    }

    /**
     * put hash map
     */
    public <K,V> void putHashAll(String key, Map<K,V> hash){
        redisTemplate.opsForHash().putAll(key,hash);
    }

    /**
     * 获取hash数据结构中某一数据
     */
    public <T> T getHash(String key, Object hashKey){
        return (T) redisTemplate.opsForHash().get(key,hashKey);
    }

    public <T> T getHash(String key, String hashKey, final Class<T> clazz) {
        Boolean flg = redisTemplate.opsForHash().hasKey(key, hashKey);
        if(!flg){
            return null;
        }
        final byte[] rawKey = serializeKey(key);
        final byte[] rawHashKey = serializeKey(hashKey);
        T objT = redisTemplate.execute(new RedisCallback<T>() {
            @Override
            public T doInRedis(RedisConnection connection) {
                byte[] rawHashValue =  connection.hGet(rawKey, rawHashKey);
                return deserializeValue(rawHashValue,clazz);
            }
        }, true);
        return objT;
    }

    public <T> T getHash(String key, String hashKey, final TypeReference<T> type) {
        final byte[] rawKey = serializeKey(key);
        final byte[] rawHashKey = serializeKey(hashKey);
        return redisTemplate.execute(connection -> {
            byte[] rawHashValue = connection.hGet(rawKey, rawHashKey);
            return (T) deserializeValue(rawHashValue, type.getClass());
        }, true);
    }



    /** 序列号 Key，Value */
    public byte[] serializeKey(String key) {
        RedisSerializer<String> keySerializer = redisTemplate.getStringSerializer();
        return keySerializer.serialize(key);
    }


    public <T> T deserializeValue(byte[] value, Class<T> clazz) {
        return JacksonUtil.toJavaBean(value, clazz);
    }


    public String deserializeKey(byte[] key) {
        RedisSerializer<String> stringSerializer = redisTemplate.getStringSerializer();
        return stringSerializer.deserialize(key);
    }

    public Set<String> getAllKey(String keys) {
        return redisTemplate.keys(keys);
    }
}
